import { CloseOutlined } from '@ant-design/icons';
import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { nmAppErrorLog, nmAppLog } from '@app-nextmesh/common/utils';
import { showLoading } from '@app-nextmesh/components';
import { ColorBar } from '@app-nextmesh/components/Bars/ColorBar';
import {
  Entity,
  FormField,
  FormNativeSelect,
} from '@app-nextmesh/components/Forms/components';
import { ISelectOption } from '@app-nextmesh/components/types';
import { documentV2 } from '@app-nextmesh/domain';
import { useProjectStore } from '@app-nextmesh/store/project';
import {
  ENTITY_TYPE_EDGE,
  ENTITY_TYPE_FACE,
  IResult,
  ISolve,
  ModelEventType,
} from '@app-nextmesh/types';
import { ColormapNames, events } from '@app-nextmesh/utils';
import {
  clearField,
  clearStreamline,
  deleteModels,
  renderCurves,
  renderFields,
  renderModels,
} from '@app-nextmesh/utils/renderModel';
import type { CollapseProps } from 'antd';
import { AutoComplete, Collapse, InputNumber, Select, Typography } from 'antd';
import { cloneDeep } from 'lodash';
import { evaluate } from 'mathjs';
import { useEffect, useRef, useState } from 'react';
import BaseForm from '../../BaseForm';
import { IProps } from '../types';
import AnimationUi from './AnimationUi';
import ResultSelect from './ResultSelect';
import {
  PlotCmdData,
  PlotCmdName,
  PlotCmdParam,
  PlotCmdType,
  PlotFormData,
  PlotSource,
} from './types';

interface IVisualizationProps extends IProps {
  formIndex?: number;
}

export default function VisualizationPlotForm({
  formIndex,
  data: initData,
  onClose,
  onUpdate,
  onUpdateTitle,
}: IVisualizationProps) {
  const projectInfo = useProjectStore((state) => state.projectInfo);
  // 完整的visualization表单数据，用于提交到服务器
  const [data, setData] = useState<any>({});
  const reset = useRef({ reset: true, oldTitle: '' });
  const [formData, setFormData] = useState<PlotFormData>(null);
  const [colormap, setColormap] = useState<string>('');
  const [formUiList, setFormUiList] = useState<CollapseProps['items']>([]); // 处理折叠面ui数据
  const formUiArray = useRef<CollapseProps['items']>([]);
  const [formDataList, setFormDataList] = useState<PlotCmdData[]>([]); // 处理后端数据格式
  const formDataArray = useRef<PlotCmdData[]>([]);
  const [activeKey, setActiveKey] = useState([1]); // 记录需打开折叠面板key
  const [filedData, setFiledData] = useState(null); // 记录场的数据
  const [currentResultInfo, setCurrentResultInfo] = useState<IResult>(null);
  const resultInfoMap = useRef(new Map<string, IResult>());
  const resultMeshesId = useRef<string[]>([]); // 保存结

  useEffect(() => {
    events.emit(ModelEventType.SetColormap, 'jet');
    return () => {
      // 重置colormap
      events.emit(ModelEventType.SetColormap, null);
      // 删除结果的网格
      deleteModels(resultMeshesId.current);
      clearField();
      clearStreamline();
    };
  }, []);

  useEffect(() => {
    hideGeometry();
    return () => {
      hideGeometry();
    };
  }, []);

  useEffect(() => {
    formUiArray.current = formUiList;
    formDataArray.current = formDataList;
    clearField();
    clearStreamline();
  }, [formUiList, formDataList]); // 添加绘图组条件时数据添加

  const hideGeometry = () => {
    // 暂时不显/隐Geometry，通过控制Geometry 的 polygon 来解决 闪烁（斑马纹）
    // if (documentManager.getMeshesData()?.length !== 0) {
    //   updateGeometriesVisible('hide');
    // }
  };

  const createPlotCmd = (name: string, type: PlotCmdType): PlotCmdData => {
    const param: PlotCmdParam = { fields: [], formulas: [''] };
    if (type === 'isosurface') {
      param.value = 5;
    } else if (type === 'streamline') {
      param.value = 100;
      param.formulas = ['', '', ''];
      param.SelectedEntities = [];
    } else if (type === 'deform') {
      param.formulas = ['', '', ''];
    }
    const cmd = {
      name,
      type,
      param,
    };
    return cmd;
  };

  // 输入框内容
  const inputChange = (
    value: string | number | string[],
    title: string,
    type: '表达式' | '平面数' | '采样数' | '选择实体',
    index?: number,
  ) => {
    const formItem = formDataArray.current.find(
      (item: PlotCmdData) => item.name === title,
    );
    switch (type) {
      case '表达式':
        formItem.param.formulas[index] = value as string;
        break;
      case '选择实体':
        formItem.param.SelectedEntities = value as string[];
        break;
      default:
        formItem.param.value = Number(value);
        break;
    }
    setFormDataList([...formDataArray.current]);
  };

  // 删除方法
  const deleteFormData = (value: number, name: string) => {
    if (name.includes('流线')) {
      hideGeometry();
    }
    const newFormList = formUiArray.current.filter(
      (item) => item.key !== value,
    );
    const newFormDrawing = formDataArray.current.filter(
      (item) => item.name !== name,
    );
    setFormUiList(newFormList);
    setFormDataList(newFormDrawing);
  };

  // ui删除Icon
  const deleteExtra = (value: number, name: string) => (
    <CloseOutlined
      onClick={(event) => {
        event.stopPropagation();
        deleteFormData(value, name);
      }}
    />
  );

  // 获取折叠面板数据
  const createCollapse = (
    maxValue: number,
    item: PlotCmdData,
    value: PlotCmdName,
    result?: IResult,
  ) => {
    const solveList: any =
      result !== undefined ? result?.solve[0] : currentResultInfo.solve[0];
    const filedList = [];
    solveList.field.forEach((item) => {
      item.Variable.forEach((v) => {
        filedList.push({
          value: item.type !== 'scalar' ? item.name + '_' + v : item.name,
        });
      });
    });
    setFiledData({ filedList, fieldName: solveList.field });
    const label = item.name;
    const param = item.param;
    return {
      key: maxValue,
      label: label,
      extra: deleteExtra(maxValue, label),
      children: (
        <>
          {value !== '动画' && (
            <AutoComplete
              style={{ width: '100%' }}
              options={filedList}
              placeholder='表达式'
              defaultValue={param.formulas[0]}
              filterOption={(inputValue, option) =>
                option!.value
                  .toUpperCase()
                  .indexOf(inputValue.toUpperCase()) !== -1
              }
              onChange={(e) => {
                inputChange(e, label, '表达式', 0);
              }}
            />
          )}
          {value === '等值面' && (
            <InputNumber
              disabled={projectInfo.readonly}
              style={{ width: '295px' }}
              placeholder='平面数'
              defaultValue={param.value}
              onChange={(value) => {
                inputChange(value, label, '平面数');
              }}
            />
          )}
          {(value === '形变' || value === '流线') && (
            <>
              <AutoComplete
                style={{ width: '100%' }}
                options={filedList}
                placeholder='表达式'
                defaultValue={param.formulas[1]}
                filterOption={(inputValue, option) =>
                  option!.value
                    .toUpperCase()
                    .indexOf(inputValue.toUpperCase()) !== -1
                }
                onChange={(e) => {
                  inputChange(e, label, '表达式', 1);
                }}
              />
              <AutoComplete
                style={{ width: '100%' }}
                options={filedList}
                placeholder='表达式'
                defaultValue={param.formulas[2]}
                filterOption={(inputValue, option) =>
                  option!.value
                    .toUpperCase()
                    .indexOf(inputValue.toUpperCase()) !== -1
                }
                onChange={(e) => {
                  inputChange(e, label, '表达式', 2);
                }}
              />
            </>
          )}
          {value === '流线' && (
            <>
              <InputNumber
                disabled={projectInfo.readonly}
                style={{ width: '295px' }}
                placeholder='入流场采样数'
                defaultValue={param.value}
                onChange={(value) => {
                  inputChange(value, label, '采样数');
                }}
              />
              <Entity
                disabled={projectInfo.readonly}
                switchedOn={true}
                name='Selection'
                type={[ENTITY_TYPE_EDGE, ENTITY_TYPE_FACE]}
                value={param.SelectedEntities}
                onChange={(value) => {
                  inputChange(value, label, '选择实体');
                }}
                assignedValues={new Set<string>()}
                onClick={() => {}}
              />
            </>
          )}
          {value === '动画' && (
            <AnimationUi
              totalFrame={sumFrame(currentResultInfo?.solve ?? result?.solve)} // 总帧数
              onChange={(currentFrame) => {
                const { step, frame } = computeStepAndFrame(
                  currentFrame,
                  currentResultInfo?.solve ?? result?.solve,
                );
                // 掉后端接口获取每帧渲染的数据
                handleLoadVisualization(
                  formDataArray.current,
                  initData.formData.uuid,
                  formIndex,
                  step,
                  frame,
                );
              }}
            />
          )}
        </>
      ),
    };
  };

  /**
   * 基于solves，将每一步的帧数相加，求帧数总和，用于动画
   * @param solves solve数组
   * @returns 总帧数
   */
  const sumFrame = (solves?: ISolve[]) => {
    let totalFrame = 0;
    solves?.forEach((solve) => {
      if (solve.frames) {
        totalFrame += solve.frames.length;
      }
    });
    return totalFrame;
  };

  /**
   * 根据当前frame，计算step和step的frame
   * @param currentFrame 当前帧
   * @param solves solve数组
   */
  const computeStepAndFrame = (currentFrame: number, solves?: ISolve[]) => {
    let step = 0;
    let frame = 0;

    if (solves?.length > 0) {
      for (let i = 0; i < solves.length; i++) {
        const solve = solves[i];
        if (solve.frames) {
          if (currentFrame < solve.frames.length) {
            step = solve.step;
            frame = currentFrame;
            break;
          } else {
            currentFrame -= solve.frames.length;
          }
        }
      }
    }

    return { step, frame };
  };

  // 绘图组Ui处理
  const createFormUi = (
    value: PlotCmdName,
    formUiData: CollapseProps['items'],
    formData: PlotCmdData[],
    type: 'init' | 'select',
    maxKey: number,
    item?: PlotCmdData,
    result?: IResult,
  ) => {
    let valueType: PlotCmdType;
    activeKey.push(maxKey);
    setActiveKey([...activeKey]);
    let checkItRepeated = false;
    switch (value) {
      case '云图':
        valueType = 'color';
        checkItRepeated = true;
        break;
      case '等值面':
        valueType = 'isosurface';
        break;
      case '筛选':
        valueType = 'filtrate';
        break;
      case '形变':
        valueType = 'deform';
        checkItRepeated = true;
        break;
      case '流线':
        valueType = 'streamline';
        checkItRepeated = true;
        break;
      case '动画':
        valueType = 'animation';
        checkItRepeated = true;
        break;
    }
    if (type === 'select') {
      if (
        checkItRepeated &&
        formData.some(({ type }) => {
          return type === valueType;
        })
      ) {
        MessageTip.warning(`当前已存在【${value}】,请勿重复添加！`);
        return null;
      }
      item = createPlotCmd(`${value}${maxKey}`, valueType);
      formData.push(item);
    }
    setFormDataList([...formData]);
    const collapse = createCollapse(maxKey, item, value, result);
    return collapse;
  };

  useEffect(() => {
    setFormUiList([]);
    formUiArray.current = [];
    formDataArray.current = [];
    const newData = cloneDeep(initData.formData);
    setData(newData);
    let newForm: PlotFormData = undefined;
    // 获取plots字段
    const plots = newData.plots;
    const count = (plots?.length ?? 0) + 1;
    if (formIndex === undefined) {
      // 添加一项新的数据
      newForm = {
        name: '绘图组' + count,
        source: null,
        colormap: 'jet',
        commands: [],
      };
    } else {
      newForm = cloneDeep(plots[formIndex]);
      if (newForm.source) {
        if (newForm.source.step === undefined) {
          newForm.source.step = 0;
        }
        documentV2.resultInfo(newForm.source.uuid).then((result) => {
          setCurrentResultInfo(result);
          initFormUi(result);
          resultInfoMap.current.set(newForm.source.uuid, result);
        });

        handleLoadVisualization(
          newForm.commands,
          newData.uuid,
          formIndex,
          newForm.source.step,
          newForm.source.frame,
        );
      }
    }
    setFormData(newForm);
    setColormap(newForm.colormap);
    events.emit(ModelEventType.SetColormap, newForm.colormap);
    reset.current.oldTitle = newForm?.name;
    return () => {
      if (reset.current.reset) {
        // 重置标题
        onUpdateTitle?.(reset.current.oldTitle);
      }
    };
  }, [initData, formIndex, initData.formData]);

  const initFormUi = (result) => {
    const newFormArray: PlotCmdData[] = cloneDeep(
      initData.formData?.plots?.[formIndex]?.commands,
    );
    const arrayUiList = [];
    newFormArray.forEach((item, index) => {
      let title: PlotCmdName;
      if (item.type === 'color') {
        title = '云图';
      } else if (item.type === 'isosurface') {
        title = '等值面';
      } else if (item.type === 'filtrate') {
        title = '筛选';
      } else if (item.type === 'deform') {
        title = '形变';
      } else if (item.type === 'streamline') {
        title = '流线';
        // 暂时不显/隐Geometry，通过控制Geometry 的 polygon 来解决 闪烁（斑马纹）
        // if (documentManager.getMeshesData()?.length !== 0) {
        //   updateGeometriesVisible('show');
        // }
      } else if (item.type === 'animation') {
        title = '动画';
      }
      const list: any = createFormUi(
        title,
        formUiList,
        newFormArray,
        'init',
        index + 1,
        item,
        result,
      );
      if (list !== null) {
        arrayUiList.push(list);
      }
    });
    setFormUiList(arrayUiList);
  };

  const handleLoadVisualization = (
    cmds: PlotCmdData[],
    uuid: string,
    index: number,
    step: number,
    frame: number,
  ) => {
    if (!cmds || cmds.length === 0) {
      // 没有commands，则不加载对应的后处理数据
      return;
    }
    // 如果cmds中有等值面，则不显示wireframe
    const showWireframe =
      cmds.findIndex((item) => item.type === 'isosurface') < 0;
    const params = {
      index,
      type: 'plot',
      step,
      frame,
      showWireframe,
    };
    getPostporeccess(uuid, params);
  };
  const getPostporeccess = async (uuid: string, params: any) => {
    const data = await documentV2.generatePostporeccess(uuid, params);
    if (data == null) {
      return;
    }
    const modelData = data.meshes;
    if (modelData) {
      resultMeshesId.current = modelData.map((d) => d.id) ?? [];
      modelData.forEach((item) => {
        const faces = item.faces;
        item.faces = faces.filter((face) => {
          return Object.keys(face).length !== 0;
        });
      });
      await renderModels(modelData, params?.showWireframe ?? true, true);
    }
    nmAppLog('render postprocess success');
    if (data.extra) {
      events.emit(ModelEventType.SetExtra, data.extra);
    }
    if (data.fields) {
      clearField();
      renderFields(data.fields, 'jet');
      nmAppLog('render field success');
    }
    if (data.curves) {
      renderCurves(data.curves);
    }
  };
  const handleOk = async (
    data: any,
    formData: PlotFormData,
    formIndex: number,
  ) => {
    const hide = showLoading();
    try {
      // 根据source.uuid 获取 result.info，更新 fields
      const source = formData.source;
      if (!source) {
        MessageTip.error('请选择数据源');
        return false;
      }
      if (!source.fieldNames) {
        const solveObject: any = currentResultInfo?.solve?.[source.step];
        source.fieldNames = solveObject.field;
      }
      source.docId = projectInfo.uuid;
      source.version = projectInfo.version;
      data.plots[formIndex].source = source;
      const fields = filedData ?? [];

      // 校验公式，先给每个参数赋值，然后使用evaluate进行校验
      const scope = {};
      fields.filedList.forEach((item) => (scope[item.value] = 1));
      scope['x'] = 1;
      scope['y'] = 1;
      scope['z'] = 1;
      for (const item of formDataList) {
        if (!item.name.includes('动画')) {
          for (const formula of item.param.formulas) {
            if (formula === '') {
              MessageTip.error(`${item.name} 公式不能为空`);
              return false;
            }
            try {
              evaluate(formula, scope);
            } catch (e) {
              MessageTip.error(`${item.name} 公式：${formula} 不合法`);
              return false;
            }
          }
        }
      }
      data.plots[formIndex].commands = formDataList;
      const newData = await documentV2.updatePostprocess(data);
      reset.current.reset = false;
      onUpdate?.(newData);
      // 删除结果的网格
      deleteModels(resultMeshesId.current);
      handleLoadVisualization(
        data.plots[formIndex].commands,
        data.uuid,
        formIndex,
        formData.source.step,
        formData.source.frame,
      );
    } catch (err) {
      nmAppErrorLog('updatePostprocess', err);
      return false;
    } finally {
      hide();
    }
    return true;
  };
  const handleUpdateTitle = (title: string) => {
    setFormData({ ...formData, name: title });
    onUpdateTitle?.(title);
  };
  const handleSourceChange = async (source: PlotSource) => {
    let resultInfo = resultInfoMap.current.get(source.uuid);
    if (!resultInfo) {
      const hide = showLoading();
      try {
        resultInfo = await documentV2.resultInfo(source.uuid);
        resultInfoMap.current.set(source.uuid, resultInfo);
      } catch (error) {
        nmAppErrorLog('PlotForm', 'handleSourceChange', error);
      } finally {
        hide();
      }
    }
    setCurrentResultInfo(resultInfo);
    const step = source.step ?? 0;
    const frame = resultInfo?.solve?.[step]?.frames ? 0 : undefined;
    source.step = step;
    source.frame = frame;
    setFormData({
      ...formData,
      source,
    });
  };
  if (!formData) {
    return <></>;
  }

  const stepOptions: Array<ISelectOption> = currentResultInfo?.solve?.map(
    ({ step }) => ({ label: step + '', value: step + '' }),
  ); // 之所以将 step（数值）转换为字符串，是因为使用的Select组件无法显示0值，下同
  const frameOptions: Array<ISelectOption> =
    formData.source?.step !== undefined &&
    currentResultInfo?.solve?.[formData.source.step].frames?.map(
      ({ frame }) => ({
        label: frame + '',
        value: frame + '',
      }),
    );
  return (
    <>
      <BaseForm
        disabled={projectInfo.readonly}
        title={formData.name}
        onClose={onClose}
        onOk={async () => {
          formData.colormap = colormap;
          await handleOk(data, formData, formIndex);
        }}
        updateTitle={handleUpdateTitle}
      >
        <FormField label={'数据源'}>
          <ResultSelect
            source={formData.source}
            onChange={handleSourceChange}
          />
        </FormField>
        {stepOptions?.length > 0 && (
          <FormField label='步数'>
            <FormNativeSelect
              disabled={projectInfo.readonly}
              options={stepOptions}
              value={
                formData.source?.step !== undefined
                  ? formData.source.step + ''
                  : undefined
              }
              onChange={(value) => {
                const step = parseInt(value as string);
                const frmes: any = currentResultInfo.solve[step].frames;
                const frame = frmes.length === 0 ? 0 : frmes[0].frame;
                const newFormData = {
                  ...formData,
                };
                newFormData.source.step = step;
                newFormData.source.frame = frame;
                setFormData(newFormData);
              }}
            />
          </FormField>
        )}
        {frameOptions?.length > 0 && (
          <FormField label='帧数'>
            <FormNativeSelect
              disabled={projectInfo.readonly}
              options={frameOptions}
              value={
                formData.source?.frame !== undefined
                  ? formData.source.frame + ''
                  : undefined
              }
              onChange={(value) => {
                const newFormData = {
                  ...formData,
                };
                newFormData.source.frame = parseInt(value as string);
                setFormData(newFormData);
              }}
            />
          </FormField>
        )}
        <FormField label={'颜色表'}>
          <Select
            popupMatchSelectWidth={false}
            value={colormap ?? ''}
            disabled={projectInfo.readonly}
            onChange={(cm) => {
              setColormap(cm);
              events.emit(ModelEventType.SetColormap, cm);
            }}
          >
            {ColormapNames.map((name) => {
              return (
                <Select.Option
                  key={name}
                  value={name}
                >
                  <div style={{ display: 'flex', justifyContent: 'left' }}>
                    <ColorBar
                      name={name}
                      style={{
                        marginRight: '5px',
                        borderRadius: '5px',
                        width: '50px',
                        height: '20px',
                      }}
                    />
                    <Typography.Text>{name}</Typography.Text>
                  </div>
                </Select.Option>
              );
            })}
          </Select>
        </FormField>
        <FormField label={'绘图组条件'}>
          <Select
            disabled={projectInfo.readonly || !formData.source}
            style={{ width: 200 }}
            value={null}
            placeholder='请选择添加的绘图内容'
            onChange={async (value: PlotCmdName) => {
              if (value === '动画') {
                if (!formDataList.some((item) => item.name.includes('云图'))) {
                  MessageTip.warning('请先选择云图再创建动画！');
                  return;
                }

                // 添加动画时，更新一下服务器的绘图组数据，因为动画需要用到云图
                formData.colormap = colormap;
                const success = await handleOk(data, formData, formIndex);
                if (!success) {
                  return;
                }
              }
              // 暂时不显/隐Geometry，通过控制Geometry 的 polygon 来解决 闪烁（斑马纹）
              // if (value === '流线') {
              //   if (documentManager.getMeshesData()?.length !== 0) {
              //     updateGeometriesVisible('show');
              //   }
              // }
              const maxKey =
                formUiList.length === 0
                  ? formDataList.length === 0
                    ? 1
                    : formUiList.length + 1
                  : Math.max(
                      ...formUiList.map(
                        (item, index) => (item.key as number) ?? index,
                      ),
                    ) + 1;
              const list = createFormUi(
                value,
                formUiList,
                formDataList,
                'select',
                maxKey,
              );
              if (list !== null) {
                formUiList.push(list);
              }
              setFormUiList(formUiList);
            }}
            options={[
              { value: '云图', label: '云图' },
              { value: '等值面', label: '等值面' },
              // { value: '筛选', label: '筛选' },
              { value: '形变', label: '形变' },
              { value: '流线', label: '流线' },
              { value: '动画', label: '动画' },
            ]}
          />
        </FormField>

        <Collapse
          defaultActiveKey={activeKey}
          ghost
          items={formUiList}
        />
      </BaseForm>
    </>
  );
}
